package fileop;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

import org.apache.commons.io.FileUtils;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

/**
 * 文件操作工具类：
 * 文件/目录的创建、删除、移动、复制、zip压缩与解压
 * @date 2017年3月1日
 */
public class FileOperations {


	/*使用GBK编码避免压缩中文文件名乱码*/
	private static final String CHINESE_CHARSET = "GBK";
	
	/*文件读取缓冲区大小*/
	private static final int CACHE_SIZE = 1024 * 50;
	
	/**
	 * 压缩文件或文件夹为zip格式文件
	 * @param sourceFile 文件或文件夹
	 * @param zipFilePath 压缩文件输出路径(以.zip结尾)
	 * @throws Exception
	 */
	public static void zip(String sourceFile, String zipFilePath) throws Exception {
		File sourcefile = new File(sourceFile);
		if (!sourcefile.exists()) {
			throw new Exception("指定的文件不存在！");
		}
		File zipFile = new File(zipFilePath);
		if (!zipFile.getParentFile().exists()) { // 如果目标文件的父目录不存在，则创建父目录
			new File(zipFile.getParentFile().getPath()).mkdirs();
		}
		OutputStream out = new FileOutputStream(zipFilePath);
		BufferedOutputStream bos = new BufferedOutputStream(out);
		ZipOutputStream zos = new ZipOutputStream(bos);
		zos.setEncoding(CHINESE_CHARSET);// 解决中文文件名乱码问题
		String basePath = null;
		if (sourcefile.isDirectory()) {
			basePath = sourcefile.getPath();
		} else {
			basePath = sourcefile.getParent();
		}
		zipFile(sourcefile, basePath, zos);
		zos.closeEntry();
        zos.close();
        bos.close();
        out.close();
	}
	
	/**
	 * 递归压缩文件
	 * @param parentFile
	 * @param basePath
	 * @param zos
	 * @throws Exception 
	 */
	private static void zipFile(File parentFile, String basePath, ZipOutputStream zos) throws Exception {
		File[] files = new File[0];
		if (parentFile.isDirectory()) {
			files = parentFile.listFiles();
		} else {
			files = new File[1];
			files[0] = parentFile;
		}
		String pathName;
		InputStream is;
		BufferedInputStream bis;
		byte[] cache = new byte[CACHE_SIZE];
		for (File file : files) {
			// TODO 判断文件是否正在读写中，如果是，则不压缩该文件。
			if (!file.renameTo(file)) {
				continue;
			}
			if (file.isDirectory()) {
				pathName = file.getPath().substring(basePath.length() + 1) + File.separator;
				zos.putNextEntry(new ZipEntry(pathName));
				zipFile(file, basePath, zos);
			} else {
				pathName = file.getPath().substring(basePath.length() + 1);
				is = new FileInputStream(file);
				bis = new BufferedInputStream(is);
				zos.putNextEntry(new ZipEntry(pathName));
				int nRead = 0;
				while ((nRead = bis.read(cache, 0, CACHE_SIZE)) != -1) {
					zos.write(cache, 0, nRead);
				}
				bis.close();
				is.close();
			}
		}
	}
	
	/** 
     * 功能:压缩多个文件成一个zip文件 
     * @param sourceFile：源文件列表 
     * @param targetZipFile：压缩后的文件 
	 * @throws Exception 
     */  
	public static void zipFiles(File[] sourceFiles, File targetZipFile) throws Exception {
		byte[] buf = new byte[CACHE_SIZE];
		ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(targetZipFile));
		zos.setEncoding(CHINESE_CHARSET);// 解决中文文件名乱码问题
		FileInputStream fis = null;
		for (int i = 0; i < sourceFiles.length; i++) {
			fis = new FileInputStream(sourceFiles[i]);
			zos.putNextEntry(new ZipEntry(sourceFiles[i].getName()));
			int len;
			while ((len = fis.read(buf)) > 0) {
				zos.write(buf, 0, len);
			}
			if (zos != null) {
				zos.closeEntry();
			}
			if (fis != null) {
				fis.close();
			}
		}
		if (zos != null) {
			zos.close();
		}
	}

	/**
	 * 解压指定的zip压缩包到目标目录
     * 对应的是ant-1.6.5.jar 
	 * @param sourceFile 指定的zip压缩包文件
	 * @param destDir 目标目录
	 * @throws Exception
	 */
	public static void unzip(String sourceFile, String destDir) throws Exception {
		Project p = new Project();
		Expand expand = new Expand();
		expand.setProject(p);
		expand.setSrc(new File(sourceFile));
		expand.setOverwrite(false);
		expand.setDest(new File(destDir));
		expand.setEncoding(CHINESE_CHARSET);
		expand.execute();
	}   

	/**
	 * copy普通文件到目标文件夹下
	 * @param sourceFile 源文件路径
	 * @param targetDir 目标文件夹路径
	 * @throws Exception
	 */
	public static void copyFileToDir(String sourceFile, String targetDir) throws Exception {
		File source = new File(sourceFile);
		File target = new File(targetDir);
		FileUtils.copyFileToDirectory(source, target);
	}
	
	/**
	 * copy普通文件到目标文件，可实现文件同目录改名
	 * @param sourceFile 普通文件
	 * @param targetFile 目标文件
	 * @throws Exception 
	 */
	public static void copyFileToFile(String sourceFile, String targetFile) throws Exception {
		File source = new File(sourceFile);
		File target = new File(targetFile);
		FileUtils.copyFile(source, target);
	}
	
	/**
	 * copy指定文件夹到目标文件夹
	 * @param sourceDir 指定原文件夹
	 * @param targetDir 目标文件夹
	 * @throws Exception 
	 */
	public static void copyDirToDir(String sourceDir, String targetDir) throws Exception {
		File source = new File(sourceDir);
		File target = new File(targetDir); 
		FileUtils.copyDirectoryToDirectory(source, target);
	}
	
	
	/**
	 * 删除指定的文件夹(递归删除多级目录)
	 * 注意：不论文件夹是否为空都可以删除
	 * @param sourceDir 指定的文件夹路径
	 * @throws Exception
	 */
	public static void deleteDir(String sourceDir) throws Exception {
		File directory = new File(sourceDir);
		FileUtils.deleteDirectory(directory);
	}
	
	/**
	 * 删除文件或文件夹，
	 * 注意：不论文件夹是否为空都可以删除，请慎用
	 * @param sourceFilePath 指定的文件\文件夹路径
	 */
	public static void deleteFile(String sourceFilePath) {
		File source = new File(sourceFilePath);
		FileUtils.deleteQuietly(source);
	}
	
	/**
	 * 指定文件移动到目标文件，可以实现指定文件的同目录下改名，同时会删除原文件
	 * @param sourceFile 指定文件路径
	 * @param targetFile 目标文件路径
	 * @throws Exception
	 */
	public static void moveFileToFile(String sourceFile, String targetFile) throws Exception {
		File source = new File(sourceFile);
		File target = new File(targetFile);
		FileUtils.moveFile(source, target);
	}
	
	/**
	 * 指定文件移动到目标目录下，同时会删除原文件
	 * @param sourceFile 指定文件
	 * @param targetDir 目标目录
	 * @throws Exception
	 */
	public static void moveFileToDir(String sourceFile, String targetDir) throws Exception {
		File source = new File(sourceFile);
		File target = new File(targetDir);
		FileUtils.moveToDirectory(source, target, true);
	}
	
	/**
	 * 指定目录移动到目标目录，同时会删除原目录
	 * @param sourceDir 指定源目录
	 * @param targetDir 目标目录
	 * @throws Exception
	 */
	public static void moveDirToDir(String sourceDir, String targetDir) throws Exception {
		File source = new File(sourceDir);
		File target = new File(targetDir);
		FileUtils.moveDirectoryToDirectory(source, target, true);
	}
	
	/**
	 * 创建单个普通文件(不能是目录)
	 * @param descFileName 文件名，包含路径
	 * @return 如果文件创建成功，则返回true，否则返回false
	 * @throws Exception 
	 */
	public static boolean createFile(String descFileName) throws Exception {
		File file = new File(descFileName);
		if (file.exists()) { // 如果文件已经存在，则创建失败
			return false;
		}
		if (descFileName.endsWith(File.separator)) { // 如果文件路径以'/'结尾，则是目录，不能创建目录
			return false;
		}
		if (!file.getParentFile().exists()) { // 如果文件的父目录不存在，则需要创建
			// 如果文件所在的目录不存在，则创建目录
			if (!file.getParentFile().mkdirs()) { // 如果父目录创建失败，则文件创建也失败
				return false;
			}
		}
		if (file.createNewFile()) {  // 文件创建成功
			return true;
		} else { // 文件创建失败
			return false;
		}
	}
	
	/**
	 * 创建目录
	 * @param descDirName 目录名,包含路径
	 * @return 如果目录创建成功，则返回true，否则返回false
	 */
	public static boolean createDirectory(String descDirName) {
		String descDirNames = descDirName;
		if (!descDirNames.endsWith(File.separator)) {
			descDirNames = descDirNames + File.separator;
		}
		File descDir = new File(descDirNames);
		if (descDir.exists()) { // 目录已经存在，则创建失败
			return false;
		}
		if (descDir.mkdirs()) { // 创建成功
			return true;
		} else { // 创建失败
			return false;
		}
	}
	
	
	public static void main(String[] args) throws Exception {
		String sourceFolder = "D:\\ziptest";
		String destDir = "D:/aaa/按文件名过滤.zip";
//			zip(sourceFolder, destDir);
//			zip("D:\\auth建表语句.sql", destDir);
//			unzip(sourceFolder, destDir);
//			unzip2(sourceFolder, destDir);
//			deCompress(sourceFolder, destDir);
//			copy(sourceFolder, destDir);
//			move(sourceFolder, destDir);
//			copyFolder(sourceFolder, destDir);
//			copyFile(sourceFolder, destDir);
//			copyFileToDir(sourceFolder, destDir);
//			copyFileToFile(sourceFolder, destDir);
//			copyDirToDir(sourceFolder, destDir);
//			deleteDir(sourceFolder);
//			deleteFile(zipFilePath);
//			moveFileToFile(sourceFolder, destDir);
//			moveFileToDir(sourceFolder, destDir);
//			moveDirToDir(sourceFolder, destDir);
//			createFile(sourceFolder);
//			createDirectory(sourceFolder);
			zip(".xlsx", sourceFolder, destDir, true);
	}

	/**
	 * 压缩指定文件夹下符合匹配规则的文件到目标目录下
	 * @param regex 匹配规则
	 * @param sourceDir 源文件夹
	 * @param targetZipFile 目标zip文件的路径及名称
	 * @param isEnd 是否以regex匹配规则结尾，为true则表示按照扩展名过滤，false则表示按照文件名(除扩展名外)过滤
	 * @throws Exception 
	 */
	public static void zip(String regex, String sourceDir, String targetZipFile, boolean isEnd) throws Exception {
		File sourceDirFile = new File(sourceDir);
		File targetZip = new File(targetZipFile);
		if (!targetZip.getParentFile().exists()) {
			new File(targetZip.getParent()).mkdirs();
		}
		
		File[] files = null;
		
		if (!isEmpty(regex) && isEnd) {
			// 源文件夹下符合过滤条件的文件，不包含子文件夹中的文件在内
			files = sourceDirFile.listFiles(new ExtensionFilter(regex));
		} else if (!isEmpty(regex) && !isEnd) {
			// 源文件夹符合过滤条件的所有文件及文件夹的路径及名称，不包含子文件夹中的文件或文件夹在内
			files = sourceDirFile.listFiles(new NameFilter(regex));
		} else {
			// 源文件夹下的所有的文件/文件夹的路径及名称，不包含子文件夹内的文件/文件夹
			files = sourceDirFile.listFiles();
		}
		System.out.println(Arrays.toString(files));
		if (files != null && files.length > 0) {
			zipFiles(files, targetZip);
		} else {
			throw new Exception("不存在符合匹配规则的文件！");
		}
		
	}
	
	
	private static boolean isEmpty(String str) {
		if (str == null || "".equals(str)) {
			return true;
		}
		return false;
	}
	
}



/**文件名过滤器*/
class NameFilter implements FilenameFilter {
	/*匹配规则*/
	private String regex;
	
	public NameFilter(String regex) {
		this.regex = regex;
	}
	@Override
	public boolean accept(File dir, String name) {
		File f = new File(dir.getPath() + File.separator + name);
		if (f.isDirectory()) {
			return false;
		}
		if (name.contains(".")) {
			name = name.substring(0, name.lastIndexOf(".")); // 将文件名的扩展名去掉
		}
		if (name.indexOf(regex) > -1) {
			return true;
		}
		return false;
	}
	
}
/**扩展名过滤器*/
class ExtensionFilter implements FileFilter {

	/*匹配规则*/
	private String regex;
	
	public ExtensionFilter(String regex) {
		this.regex = regex;
	}
	
	@Override
	public boolean accept(File pathname) {
		if (pathname.isDirectory()) {
			return false;
		}
		String filename = pathname.getName();
		boolean isMatched = filename.endsWith(regex);
		if (pathname.isFile() && isMatched) {
			return true;
		}
		return false;
	}
}